package com.jacky.ocr_plugin.camera;

import android.content.Context;
import android.graphics.Rect;
import android.hardware.camera2.CameraAccessException;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.util.Size;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;

import com.jacky.util.OcrLogger;
import com.jacky.util.FilePathUtils;
import com.jacky.util.OcrRectManager;
import com.jacky.util.OnOcrRectCallback;
import com.jacky.util.OnOcrRectErrorCallback;
import com.yunmai.cc.idcard.controler.OcrConstant;


import java.io.File;
import java.util.HashMap;

/**
 * 管理camera2的相机进行ocr识别
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CustomOcrRectView3 {

    private final int TAKE_PREVIEW = 199;
    private final int START_PREVIEW = 198;
    private HandlerThread mThread;
    private Handler mThreadHandler;
    private Runnable mThreadRun;
    private String mOcrImgFilePath;
    private String mOcrHeadImgFilePath;
    private boolean scanEnable = true;
    private boolean takePhotoRotated = true;
    private boolean portraitToLandscape = true;
    private int type = 0;
    private boolean mUseOrientation;
    private Rect overlayRec;
    private Rect mUsedRect;
    private final OcrRectManager mRectManager;

    private CameraView cameraView;
    private final OnOcrRectCallback callback;
    private final OnOcrRectErrorCallback errorCallback;
    private final Context context;

    private boolean isTakePictureFromPreview;
    private boolean isTakePictureWithRect;
    private int viewWidth;
    private int viewHeight;
    private boolean canOcrRect;
    //正在处理拍照的数据，防止多次处理数据返回
    private boolean mHandlingData = false;

    public CustomOcrRectView3(Context context, CameraView cameraView,
                              OnOcrRectCallback callback,
                              OnOcrRectErrorCallback errorCallback) {
        this.cameraView = cameraView;
        this.callback = callback;
        this.errorCallback = errorCallback;
        this.context = context;
        mRectManager = OcrRectManager.get(context,mHandler);
        cameraView.setImageListener((data, rotation) -> {
            //线程名 OcrCamera
//            OcrLogger.d("setImageListener",data.length,rotation,isTakePictureFromPreview,isCanOcrRect());
            if(data.length == 0){
                OcrLogger.d("image listener data.length is empty",data.length,rotation, isTakePictureFromPreview,isCanOcrRect());
                return;
            }
            if(mHandlingData){
                OcrLogger.d("onImageListener mHandlingData true");
                return;
            }
            if(isTakePictureFromPreview){
                jpegImageListener(data,rotation);
            }else if(isCanOcrRect()) {
                mRectManager.ocrRectData(data);
                //mRectManager.testForSaveImage(data);
            }
        });
        mHandler.postDelayed(this::updateRect,300);
    }

    private void jpegImageListener(byte[] data,int rotation){
        //OcrLogger.d("setJpegListener",isTakePictureFromPreview, data.length,rotation);
        if(isTakePictureFromPreview){
            try {
                onPreviewCallback(data,rotation);
            } catch (CameraAccessException e) {
                error("cameraError","图片保存失败");
                e.printStackTrace();
            }
        }
    }

    public CameraView getCameraView(){
        return cameraView;
    }

    public boolean hasCameraView(){
        return cameraView != null;
    }

    public void setParams(HashMap<String,Object> params){
        OcrLogger.d("setParams",params);
        if(params.containsKey("scanEnable")) {
            Boolean en = (Boolean) params.get("scanEnable");
            if(en != null) scanEnable = en;
        }
        if(params.containsKey("type")) {
            Integer t = (Integer) params.get("type");
            if(t != null) type = t;
        }
        if(params.containsKey("takePhotoRotated")) {
            Boolean t = (Boolean) params.get("takePhotoRotated");
            if(t != null) takePhotoRotated = t;
        }
        if(params.containsKey("ocrImgPath")) {
            String path = (String) params.get("ocrImgPath");
            if(path != null) mOcrImgFilePath = path;
        }
        if(params.containsKey("ocrHeadPath")) {
            String path = (String) params.get("ocrHeadPath");
            if(path != null) mOcrHeadImgFilePath = path;
        }
        if(params.containsKey("androidUseImageOrientation")) {
            setUseImageOrientation((Boolean) params.get("androidUseImageOrientation"));
        }
        //竖向转横向识别ocr：如手机竖向页面，转横向识别传true；pad是竖向识别传 false
        if(params.containsKey("portraitToLandscape")) {
            Boolean can = (Boolean) params.get("portraitToLandscape");
            if(can != null) {
                portraitToLandscape = can;
                updateRect();
            }
        }
        if(params.containsKey("viewWidth") || params.containsKey("viewHeight")){
            Integer w = (Integer) params.get("viewWidth");
            Integer h = (Integer) params.get("viewHeight");
            viewWidth =  w == null ? 0: w;
            viewHeight =  h == null ? 0: h;
            updateRect();
        }
    }

    /**
     * 拍照的图片，是否使用重力感应旋转图片
     */
    public void setUseImageOrientation(Boolean useOrientation){
        mUseOrientation = useOrientation != null && useOrientation;
    }

    public void setRecognizeRect(Rect recognizeRect) {
        //mUsedRect = getViewfinder(recognizeRect,portraitToLandscape);
        overlayRec = recognizeRect;
        updateRect();
    }

    public void takePicture(){
        if(cameraView == null  || cameraView.isDestroy()){
            return;
        }
        isTakePictureFromPreview = true;
        cameraView.takePicture();
    }
    /**
     * 摄像头 拍照图片 根据rect进行截取
     */
    public void takePhotoCameraWithRect(){
        isTakePictureWithRect = true;
        takePicture();
    }

    /**
     * 切换摄像头
     */
    public void switchCamera(){
        if(cameraView != null){
            cameraView.switchCamera();
            startDecoding();
            mHandler.postDelayed(this::updateRect,120);
        }
    }

    public boolean toggleFlashLight(){
        if(cameraView == null)return false;
        return cameraView.toggleFlashLight();
    }
//
//    public void pausePreview(){
//        if(cameraView == null)return;
//        try {
//            cameraView.pausePreview();
//        } catch (CameraAccessException e) {
//            e.printStackTrace();
//        }
//    }
//
//    public void resumePreview(){
//        if(cameraView == null)return;
//        cameraView.resumePreview();
//    }

    public void pause() {
        if(cameraView == null || cameraView.isDestroy())return;
        stopDecoding();
        //cameraView.close();
        cameraView.pause();
    }

    public void resume(){
        if(cameraView == null || cameraView.isDestroy())return;
//        cameraView.openCamera();
        cameraView.resume();
        startDecoding();
    }

    public void close() {
        if(cameraView != null && !cameraView.isDestroy()) cameraView.close();
        stopDecoding();
    }

    public void dispose(){
//        close();
        if(cameraView != null) cameraView.dispose();
        cameraView = null;
        if(mThread != null){
            mThread.quitSafely();
            mThread = null;
        }
        if(mThreadHandler != null) {
            mThreadHandler = null;
        }
    }

    private void updateRect(){
        Size previewSize = cameraView.getPreviewSize();
        if(previewSize == null || previewSize.getWidth() == 0 || previewSize.getHeight() == 0)return;
        HashMap<String,Object> map = new HashMap<>();
        map.put("methodName","updatePreviewSize");
        map.put("width",previewSize.getWidth());
        map.put("height",previewSize.getHeight());
        handleResultCallback(map);
        mUsedRect = OcrRectManager.getViewfinder(
                overlayRec,
                viewWidth,
                viewHeight,
                previewSize);
        mRectManager.setOcrRect(
                previewSize.getWidth(),
                previewSize.getHeight(),
                mUsedRect);
    }

    private void onPreviewCallback(byte[] data,int rotation) throws CameraAccessException {
        if(cameraView == null || cameraView.isDestroy()){
            OcrLogger.d("onPreviewCallback destroy",data.length,rotation);
            return;
        }
        if(mThread == null || !mThread.isAlive()){
            mThread = new HandlerThread("savePicture");
            mThread.setUncaughtExceptionHandler(((t, e) -> {
                OcrLogger.d("ocr rect view3,thread 'savePicture' error");
                OcrLogger.d(e);
            }));
            mThread.start();
            mThreadHandler  = new Handler(mThread.getLooper());
        }
        if(mThreadRun != null) mThreadHandler.removeCallbacks(mThreadRun);
        //线程处理数据
        mHandler.post(()->{
            stopDecoding();
            try {
                if(cameraView != null)cameraView.pausePreview();
            } catch (CameraAccessException e) {
                e.printStackTrace();
            }
        });
        mHandlingData = true;
        File file = FilePathUtils.getFile(context, mOcrImgFilePath, "ocr_img.jpg");
        mThreadRun = new CustomOcrRectView2.PreviewDataRunnable(
                data,
                cameraView.getPreviewWidth(),
                cameraView.getPreviewHeight(),
                file,
                mUseOrientation ? rotation : (portraitToLandscape ? 90 : 0),
//                ImageFormat.JPEG,//图片格式的数据
                isTakePictureWithRect ? mUsedRect : null,
                () -> mHandler.post(() -> {
                    isTakePictureFromPreview = false;
                    isTakePictureWithRect = false;
                    mHandlingData = false;
                    //如果用户直接关闭页面，这时又resumePreview刷新预览 会出现问题
                    handleResultCallback(file.getAbsolutePath());
                }));//new TakePictureRun(data);
        mThreadHandler.post(mThreadRun);
        //拍一次照片，开启再次预览
//        mMainHandler.postDelayed(()->mCameraPreviewView.startPreview(),350);
    }

    private void handleResultCallback(HashMap<String, Object> data){
        if (callback != null) {
            callback.callback(data);
        }
    }

    private void handleResultCallback(String imgPath) {
        HashMap<String, Object> map = new HashMap<>();
        map.put("imgPath", imgPath);
        if (callback != null) {
            callback.callback(map);
        }
        mHandler.sendEmptyMessageDelayed(START_PREVIEW,800);
    }

    public void startDecoding(){
        if(getCameraView() == null || getCameraView().isDestroy())return;
        setCanOcrRect(scanEnable);
        mHandler.sendEmptyMessage(TAKE_PREVIEW);
    }

    public void stopDecoding(){
        setCanOcrRect(false);
        mHandler.removeMessages(TAKE_PREVIEW);
        mHandler.removeMessages(START_PREVIEW);
    }

    private boolean isCanOcrRect(){
        return canOcrRect;
    }

    private void setCanOcrRect(boolean can){
        canOcrRect = can;
    }

    private void error(String code, String message) {
        OcrLogger.d("ocrRectView", "error code " + code + " message " + message);
        if (errorCallback != null) {
            errorCallback.callback(code, message);
        }
    }

    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
//            OcrLogger.d("main handler msg",msg.what,cameraView == null,"isDestroy",cameraView==null||cameraView.isDestroy());
            switch (msg.what) {
                case TAKE_PREVIEW:
                    if(isCanOcrRect() && cameraView != null && !cameraView.isDestroy()) {
                        cameraView.takePicture();
                        sendEmptyMessageDelayed(TAKE_PREVIEW,1500);
                    }else {
                        OcrLogger.d("not take preview, can ocr",isCanOcrRect(),"view is null",cameraView == null,"isDestroy",cameraView!=null&&cameraView.isDestroy());
                    }
                    break;
                case START_PREVIEW:
                    if(cameraView != null && !cameraView.isDestroy()){
                        cameraView.resumePreview();
                        startDecoding();
                    }
                    break;
                case OcrConstant.TAKE_PREVIEW_DATA_OK:
                    byte[] data = (byte[]) msg.obj;
                    mRectManager.ocrRectData(data);
                    //mRectManager.testForSaveImage(data);//测试当前数据帧 完整图及rect区图片
                    break;
                case OcrConstant.REPEAT_AUTOFOCUS:
                case OcrConstant.START_AUTOFOCUS:
                case OcrConstant.RECOGN_LINE_IN_RECT:
                    break;
                case OcrConstant.RECOGN_OK:
                    //识别成功，进行处理数据
                    stopDecoding();
                    mHandler.removeMessages(OcrConstant.TAKE_PREVIEW_DATA_OK);
                    String imgFile = FilePathUtils.getFile(context,mOcrImgFilePath, "ocr_img.jpg").getAbsolutePath();
                    String headFile = FilePathUtils.getFile(context,mOcrHeadImgFilePath, "ocr_head.jpg").getAbsolutePath();
                    String info = mRectManager.getRectResult(imgFile,headFile);
                    if(info != null){
                        handleResultCallback(OcrRectManager.handleResultCallback(info, imgFile, headFile));
                    }
                    //让其可以继续识别
                    postDelayed(()->startDecoding(),300);
                    break;
                case OcrConstant.RECOGN_EG_TIME_OUT:
                    error("3", "引擎过期，请尽快更新！");
                    break;
                case OcrConstant.RECOGN_EG_LICENSE:
                    error("3", "授权失败！");
                    break;
                case OcrConstant.RECOGN_EG_INIT_ERROR:
                    error("3", "引擎初始化失败！");
                    break;
                default:
                    //if(cameraView != null)cameraView.resumePreview();
                    OcrLogger.d("tag","flutter to android, ocr handler what "+msg.what);
                    break;
            }
        }


    };
}
